package com.lzx.hbh_system.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lzx.hbh_system.bo.SysMenu;
import com.lzx.hbh_system.bo.SysRole;
import com.lzx.hbh_system.bo.SysRoleMenu;
import com.lzx.hbh_system.bo.filter.RoleFilter;
import com.lzx.hbh_system.bo.filter.Userfilter;
import com.lzx.hbh_system.dto.RouteDto;
import com.lzx.hbh_system.dto.UserDto;
import com.lzx.hbh_system.mapper.SysMenuMapper;
import com.lzx.hbh_system.mapper.SysRoleMapper;
import com.lzx.hbh_system.mapper.SysRoleMenuMapper;
import com.lzx.hbh_system.mapper.SysUserMapper;
import com.lzx.hbh_system.service.RoleService;
import com.lzx.hbh_system.util.RedisUtils;
import com.lzx.hbh_system.util.SnowFlakeUtil;
import com.lzx.hbh_system.util.responseEntity.RespOutMsgHeader;
import com.lzx.hbh_system.util.responseEntity.ResponseView;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class RoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements RoleService {
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysRoleMenuMapper sysRoleMenuMapper;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private ModelMapper modelMapper;
    @Autowired
    private SysMenuMapper menuMapper;
    @Override
    public ResponseView getRoleByUsernameFilter(Userfilter userfilter) throws Exception {
        //初始化返回视图
        ResponseView<List<SysRole>> responseView = new ResponseView<List<SysRole>>();
        //执行查询
        List<SysRole> roleList = sysRoleMapper.selectRole_byusername(userfilter);
        //将查询的角色信息根据对应的用户名存放在redis中以便于插入角色用户关联时等判断，提高检索效率
        UserDto userDto = modelMapper.map(redisUtils.getObject(userfilter.getUserName().trim()), UserDto.class );
        List<String> roleNameList = new ArrayList<>();
        roleList.stream().forEach(roleobj->{
            roleNameList.add(roleobj.getRoleName());
        });
        userDto.setRoleName(roleNameList);
        //保存进入redis中
        System.out.println(" "+userDto.getIslogin()+" "+userDto.getSalt()+" "+userDto.getLifetime()+" "+userDto.getStatus());
        redisUtils.set(userfilter.getUserName().trim(),userDto);
        //返回消息设置
        //暂时没有其他判断条件约束，所有结果都查询成功。
        responseView.setRespOutMsgHeader(RespOutMsgHeader.success(roleList.size(),true,"查询成功！"));
        responseView.setMain(roleList);
        return responseView;
    }

    /**
     * JWT token认证 内部使用
     * @param userName
     * @return
     */
    @Override
    public SysRole getRoleByUsername(String userName) {
        return sysRoleMapper.searchByUsername(userName);
    }
    @Override
    public ResponseView getAllRoleFilter(RoleFilter roleFilter) {
        // 初始化返回视图
        ResponseView responseView = new ResponseView<Object[]>();
        // 判断是否开启分页 若为true 则分页 否则查询所有
        if(!roleFilter.isPage()){
            // 从redis中获取角色列表，若没有再去数据库中获取并更新至redis中
            List<Object> c = redisUtils.getList("roleList");
            List<SysRole> roleList = new ArrayList<>();
            if (c != null && c.size() != 0){ // redis 不为空
                for (int i = 0; i < c.size(); i++) {
                    SysRole sysRole = JSON.parseObject(c.get(i).toString(),SysRole.class);
                    roleList.add(sysRole);
                }
            }else{ // 否则数据库中获取
                roleList = sysRoleMapper.selectList(null);
                redisUtils.delete("roleList");
                redisUtils.lRightPushAll("roleList",roleList);
            }
            responseView.setMain(roleList.toArray());
            responseView.setRespOutMsgHeader(RespOutMsgHeader.success(roleList.size(),true,"查询成功！"));
            // 只是在redis数据不存在才存放，若存在且数据库数据已经更新了的情况下，redis与数据库的数据会不一致
            return responseView;
        }
        // 分页 - index 从 1 开始 ，size 每页数据条数
        Page<SysRole> page = new Page<>(roleFilter.getPageIndex(),roleFilter.getPageSize());
        // queryWrapper 过滤结果 具体用法详情：https://blog.csdn.net/qq_18671415/article/details/109071446
        Page<SysRole> rolePage = new Page<>();
        List<SysRole> roleList = new ArrayList<>();
        // redis 分页
        List<Object> c = redisUtils.getList("roleList",roleFilter.getPageIndex(),roleFilter.getPageSize());
        if (c != null){ // redis 不为空
            for (int i = 0; i < c.size(); i++) {
                SysRole sysRole = JSON.parseObject(c.get(i).toString(),SysRole.class);
                roleList.add(sysRole);
            }
        }else{ // 否则数据库中获取 - mybatis-plus 内置分页
            rolePage = sysRoleMapper.selectPage(page,null);
        }
        // 设置查询结果，并返回消息
        responseView.setMain(roleList.size() != 0 ? roleList.toArray() : rolePage.getRecords().toArray());
        responseView.setRespOutMsgHeader(RespOutMsgHeader.success(roleList.size() != 0 ? roleList.size() : rolePage.getRecords().size(),true,"查询成功！"));
        return responseView;
    }

    /**
     * 增加一个角色信息 同时批量关联对应的路由菜单信息
     * @param roleFilter
     * @return
     */
    @Override
    public ResponseView addOneRoleFilter(RoleFilter roleFilter) {
        //初始化返回视图
        ResponseView<Object> responseView = new ResponseView<>();
        SysRole role = new SysRole(); // 初始化角色对象
        String role_id = SnowFlakeUtil.getId(); // 生成主键
        modelMapper.map(roleFilter.getRole(),role); // 拷贝信息
        role.setId(role_id); // 设置唯一数据编号
        role.setParentCode("0"); // 默认为一级主菜单，子菜单需要手动再次添加
        role.setRoleCode("role_"+role_id); // 设置主键
        // 校验是否填写角色名称角色描述
        if ("".equals(role.getRoleName()) && "".equals(role.getRoleDescription())){
            responseView.setRespOutMsgHeader(RespOutMsgHeader.error(false,"角色名称和角色描述不能为空，请检查！",500));
            return responseView;
        }
        // 校验即将添加的数据是否重复
        if (redisUtils.getList("roleList") != null) { // redis 存在改角色名称时
            List<SysRole> roleList = getRedisRoleList();
            roleList = roleList.stream().filter(roleobj-> Objects.equals(roleobj.getRoleName(), roleFilter.getRoleName())).collect(Collectors.toList());
            if (roleList.size() != 0){
                responseView.setRespOutMsgHeader(RespOutMsgHeader.error(false,"角色名称重复，请检查！",500));
                return responseView;
            }
        }// redis不存在改角色名,从数据库中查询
        if (sysRoleMapper.searchByRoleName(roleFilter.getRoleName()) != null) {
            responseView.setRespOutMsgHeader(RespOutMsgHeader.error(false,"角色名称重复，请检查！",500));
            return responseView;
        }
        // 保存可以添加的数据
        List<SysRole> roleList = getRedisRoleList();
        if (roleList != null){
            roleList.add(role);
            // 保存redis
            redisUtils.delete("roleList"); // 先删除内容，再赋值，否则会不断拼接新数据出现重复
            redisUtils.lRightPushAll("roleList",roleList);
        }
        // 保存数据库
        sysRoleMapper.insert(role);
        // 添加关联 菜单 和 角色 中间表
        insertMenuArole(role,getMenuList(roleFilter.getRoutes()));
        responseView.setRespOutMsgHeader(RespOutMsgHeader.success(1,true,"角色信息插入成功！"));
        return responseView;
    }
    public void insertMenuArole(SysRole role, List<SysMenu> menuList){
        // 初始化角色路由菜单关联对象
        SysRoleMenu roleMenu = new SysRoleMenu();
        roleMenu.setValiFlag("1"); // 默认有效
        roleMenu.setRoleCode(role.getRoleCode()); // 设置关联的角色编号
        if(menuList.size() == 0 ){ // 如果不加入关联路由的角色 不做任何操作
            return;
        }
        menuList.forEach(menu->{
            roleMenu.setRoleMenuCode(SnowFlakeUtil.getId()); // 设置主键
            roleMenu.setMenuCode(menu.getMenuCode()); // 设置关联的菜单编号
            sysRoleMenuMapper.insert(roleMenu);
        });
    }
    public List<SysRole> getRedisRoleList() {
        List<SysRole> roleList = new ArrayList<>();
        // redis 分页
        List<Object> c = redisUtils.getList("roleList");
        if (c != null) { // redis 不为空
            for (int i = 0; i < c.size(); i++) {
                SysRole sysRole = JSON.parseObject(c.get(i).toString(), SysRole.class);
                roleList.add(sysRole);
            }
        }else{
            log.error("请注意，Redis中，key: rolleList , value : "+c);
        }
        return roleList;
    }

    @Override
    public ResponseView modifyOneRoleFilter(RoleFilter filter) {
        // 初始化返回视图
        ResponseView responseView = new ResponseView<>();
        // 校验
        if (filter.getRole() == null){
            responseView.setRespOutMsgHeader(RespOutMsgHeader.error(false,"修改对象不能为空！",500));
            return responseView;
        }
        // 操作
        // 更新redis数据
        List<SysRole> roleList = getRedisRoleList();
        List<SysRole> newRoleList;
        newRoleList = roleList.stream().filter(role-> !Objects.equals(role.getRoleCode(), filter.getRole().getRoleCode())).collect(Collectors.toList());
        newRoleList.add(filter.getRole());
        List<RouteDto> routeDtos = filter.getRoutes();
        List<SysMenu> menuList = getMenuList(routeDtos);
         // 问题：前端修改传入的route对象缺少 menuCode进行关联
        //要么前端修改传入对象参数
        //要么后端映射传入对象数据再进行逻辑处理
        if (menuList.size() != 0){
            if (menuMapper.queryMenuByRoleCode(filter.getRole().getRoleCode()).size() != 0){ // 对应的角色原来没有绑定路由菜单
                // 先删除
                // 原来有绑定路由菜单 - 修改角色菜单关联表 -- 简单粗暴的方法
                sysRoleMenuMapper.deleteByRoleCode(filter.getRole().getRoleCode());
            }
            // 再添加 如果没有 直接添加
            insertMenuArole(filter.getRole(),menuList);
        }else{
            sysRoleMenuMapper.deleteByRoleCode(filter.getRole().getRoleCode()); // 删除 相当于没有绑定任何菜单路由
        }
        // 重新保存新的角色列表
        redisUtils.delete("roleList");
        redisUtils.lRightPushAll("roleList",newRoleList);
        // 更新数据库中
        sysRoleMapper.updateById(filter.getRole());
        responseView.setRespOutMsgHeader(RespOutMsgHeader.success(1,true,"角色信息修改成功！"));
        return responseView;
    }

    /**
     * 遍历路由菜单映射成菜单对象
     * @param routeDtos
     * @return
     */
    public List<SysMenu> getMenuList(List<RouteDto> routeDtos){
        List<SysMenu> menuList = new ArrayList<>();
        routeDtos.forEach(routeDto -> {
            SysMenu sysMenu = menuMapper.selectById(routeDto.getMenukey());
            menuList.add(sysMenu);
        });
        return menuList;
    }
    @Override
    public ResponseView deleteOneRoleFilter(RoleFilter roleFilter) {
        // 初始化返回视图
        ResponseView responseView = new ResponseView<>();
        // 校验
        if (roleFilter.getRoleList() == null) {
            responseView.setRespOutMsgHeader(RespOutMsgHeader.error(false,"删除的对象主键列表至少为一个！",500));
            return responseView;
        }
        // 操作
        // 删除对应redis数据
        List<SysRole> dropList = roleFilter.getRoleList();
        List<SysRole> roleList = getRedisRoleList();
        List<SysRole> dropRoleList = new ArrayList<>();
        if (roleList != null){
            dropList.forEach(role ->{
                SysRole role1 = roleList.stream().filter(roleobj->roleobj.getRoleCode().equals(role.getRoleCode())).collect(Collectors.toList()).get(0);
                dropRoleList.add(role1);
            });
            roleList.removeAll(dropRoleList); // 删除条件集合中包含的元素
        }
        // 从新保存删除剩余的角色列表到redis中
        redisUtils.delete("roleList"); // 先删除内容，再赋值，否则会不断拼接新数据出现重复
        redisUtils.lRightPushAll("roleList",roleList);

        // 删除对应角色关联菜单记录
        dropList.forEach(role->{
            // 删除数据库的数据
            sysRoleMapper.deleteById(role.getRoleCode());
            sysRoleMenuMapper.deleteByRoleCode(role.getRoleCode());
        });
        responseView.setRespOutMsgHeader(RespOutMsgHeader.success(dropList.size(),false,"批量删除成功！"));
        return responseView;
    }

    /**
     * 根据菜单编号 获取对应绑定的角色对象信息
     * @param roleFilter
     * @return
     */
    @Override
    public ResponseView getRoleByMenuCode(RoleFilter roleFilter) {
        // 初始化返回视图
        ResponseView responseView = new ResponseView<>();
        // 校验
        if (roleFilter.getMenuCode() == null) {
            responseView.setRespOutMsgHeader(RespOutMsgHeader.error(false,"查询的菜单编号不存在！",500));
            return responseView;
        }
        String menuCode = roleFilter.getMenuCode();
        List<SysRole> roleList = sysRoleMapper.searchByMeanCode(menuCode);
        responseView.setMain(roleList.toArray());
        responseView.setRespOutMsgHeader(RespOutMsgHeader.success(roleList.size(),true,"查询成功！"));
        return responseView;
    }
}
